#include "Object.h"

#include "Asset/AssetPath.h"
#include "Reflection/PropertyList.h"
#include "Reflection/Type.h"
#include "Reflection/GarbageCollectable.h"
#include "ObjectPathUtils.h"

SObjectHead *RObject::GetObjectHead() const
{
    return  SelfHead;
}

RObject::RObject()
{
    SelfHead = (SObjectHead *)(((uint8_t*)this) -sizeof(SObjectHead));
}


RObject::~RObject()
{
};


void RObject::SetOwner(RObject* InOwner)
{
    auto Head=GetObjectHead();
    
    if(Head->Owner)
    {
        Head->Owner->RemoveChildFromChildren(this);
    }

    Head->Owner=InOwner;

    if(Head->Owner)
    {
        Head->Owner->AddChildToChildren(this);
        Head->Owner->MakeSureChildNameUnique(this);
    }
}

void RObject::RemoveChildFromChildren(RObject *Child)
{
    GetObjectHead()->Children.Remove(Child);
}

void RObject::AddChildToChildren(RObject *Child)
{
    GetObjectHead()->Children.Add(Child);
}

void RObject::MakeSureChildNameUnique(RObject *InChild)
{
    auto Head=GetObjectHead();

    //make a unique name
    TVector<uint32_t> NamePostfixs;

    for(auto& Child : Head->Children )
    {
        if(Child==InChild)
        {
            continue;
        }

        if(Child->GetRawName()==InChild->GetRawName())
        {
            NamePostfixs.Add(Child->GetNamePostfix());
        }
    }

    if(NamePostfixs.Empty())
    {
        InChild->GetObjectHead()->NamePostfix=0;
        return;
    }

    auto ChildeHead=InChild->GetObjectHead();
    ChildeHead->NamePostfix=NamePostfixs.Num()-1; //use NamePostfixs.Num() to make it faster
    if(NamePostfixs.Num()!=0)
    {
        while(true)
        {
            if(!NamePostfixs.Contains(ChildeHead->NamePostfix))
            {
                break;
            }
            ChildeHead->NamePostfix++;
        }
    }    
}

CString RObject::ToString()
{
    return GetClass()->GetFullName() + U"//" + GetPath();
}

void RObject::GetReferencedObjects( TForwardForwardLinkedList<RObject *> &OutReferencedObjects)
{
    //收集对象/结构体类型的引用对象
    auto Class = this->GetClass();
    if(!Class->Properties)
    {
        return;
    }
    
    //find in field
    for(auto& Field : Class->Properties->GetNonStaticFieldProperties())
    {
        auto FieldType = Field->GetType().BasicType;
        if(FieldType->GarbageCollectable)
        {
            void* FieldValue= Field->FieldProperty->GetValuePointer(this);
            FieldType->GarbageCollectable->CollectReferencedObjects(FieldValue, OutReferencedObjects);
        }
    }    
}

CString RObject::GetNodeName() const
{
    return GetObjectHead()->GetNodeName();
}

void RObject::SetNodeName(const CString &InNodeName)
{
    return GetObjectHead()->SetNodeName(InNodeName);
}

RObject *RObject::FindChild(const CString &InNodeName)
{
    return GetObjectHead()->FindChild(InNodeName);
}

CString RObject::GetPath() const
{
    if(GetOwner())
    {
        return GetOwner()->GetPath() +WH::ObjectPathSeparator + GetNodeName();
    }
    else
    {
        return GetNodeName();
    }
}

const SAssetPath *RObject::GetAssetPath() const
{
    if(GetOwner())
    {
        return GetOwner()->GetAssetPath();
    }
    else
    {
        return nullptr;
    }
}

CString RObject::GetRawName()
{
    return GetObjectHead()->RawName;
}

void RObject::SetRawName(CString InRawName)
{
    GetObjectHead()->RawName=InRawName;
    if(GetObjectHead()->Owner)
    {
        GetObjectHead()->Owner->MakeSureChildNameUnique(this);
    }
}

RObject *SObjectHead::FindChild(const CString &InNodeName)
{
    for(auto& Child : Children)
    {
        if(Child->GetNodeName()==InNodeName)
        {
            return Child;
        }
    }

    return nullptr;
}

CString SObjectHead::GetNodeName()
{
    if(NamePostfix==0)
    {
        return RawName;
    }
    else
    {
        return RawName + U"_" + CString(NamePostfix);
    }
}

void SObjectHead::SetNodeName(const CString &InNodeName)
{
    //detect postfix
    int64_t Index=InNodeName.FindLast(U'_');

    if(Index!=-1)
    {
        CString Postfix=InNodeName.SubString(Index+1);
        if(Postfix.IsDigit())
        {
            RawName=InNodeName.SubString(0,Index);
            NamePostfix=Postfix.ToInt64();
            return;
        }

    }

    RawName=InNodeName;
    NamePostfix=0;   
}
